package scaladuct

import java.lang.Class

/**
 * <p>
 * Created by IntelliJ IDEA.
 * User: zslajchrt
 * Date: Dec 2, 2010
 * Time: 11:12:12 PM
 */
class StackConfigPage[X](val bottomPage: ConfigPage[X], val leaves: List[StackPageLeafFactory]) extends {
val topLeaf = leaves.head.newLeaf
val id = topLeaf.id
} with ConfigPage[X] {
  type Cookie = topLeaf.Cookie

  def consume(cookie: Cookie): ConfigResult[X] = {
    val newLeaves = topLeaf.consume(cookie) ::: leaves.tail
    if (newLeaves == Nil)
      NextPage(bottomPage)
    else
      NextPage(new StackConfigPage(bottomPage, newLeaves))
  }

  def getCookie() = topLeaf.getCookie

  override def canUnconsume(result: ConfigResult[X]): Boolean = result match {
    case NextPage(nextPage) => {
      if (nextPage == bottomPage) {
        topLeaf.canUnconsume(Nil)
      } else {
        val nextLeaves = nextPage.extension(classOf[StackConfigPage[X]]) match {
          case Some(stackPage) => stackPage.leaves
          case None => error("StackConfigPage expected")
        }
        val nextTopLeaves = nextLeaves.take(nextLeaves.size - this.leaves.size + 1)
        topLeaf.canUnconsume(nextTopLeaves)
      }
    }
    case _ => error("unexpected")
  }

  override def unconsume(result: ConfigResult[X]) = result match {
    case NextPage(nextPage) => {
      if (nextPage == bottomPage) {
        topLeaf.unconsume(Nil)
      } else {
        val nextLeaves = nextPage.extension(classOf[StackConfigPage[X]]) match {
          case Some(stackPage) => stackPage.leaves
          case None => error("StackConfigPage expected")
        }
        val nextTopLeaves = nextLeaves.take(nextLeaves.size - this.leaves.size + 1)
        topLeaf.unconsume(nextTopLeaves)
      }
    }
    case _ => error("unexpected")
  }

  override def cancel() = {
    topLeaf.cancel
  }

  override def extension[T](extClass: Class[T]) = {
    super.extension(extClass) match {
      case None => topLeaf.extension(extClass)
      case Some(ext) => Some(ext)
    }

  }
}

@serializable @SerialVersionUID(1L)
trait StackPageLeafFactory extends Extensible {
  def newLeaf: StackPageLeaf
}

@serializable @SerialVersionUID(1L)
trait StackPageLeaf extends Extensible {
  val id: String
  type Cookie <: AnyRef

  def getCookie: Cookie

  def consume(cookie: Cookie): List[StackPageLeafFactory]

  def canUnconsume(result: List[StackPageLeafFactory]): Boolean

  def unconsume(result: List[StackPageLeafFactory]): Unit

  def cancel(): Unit;
}

class SingletonStackPageLeafFactory(val leaf: StackPageLeaf) extends StackPageLeafFactory {
  def newLeaf = leaf
}

class FunctionalStackPageLeafFactory(leafGetter: => StackPageLeaf) extends StackPageLeafFactory {
  def newLeaf = leafGetter
}